// ------------------------------------------------------------------------ //
// Texas A&M University                                                     //
// CPSC321 Computer Architecture; Fall 2002                                 //
//                                                                          //
// $Id: ALU_behav.v,v 1.3 2002/11/14 16:06:04 miket Exp miket $             //
//                                                                          //
// Modifed by:  Mitchell Kline and Harshvardhan                             //
//         on:  April 12, 2007                                              //
// ------------------------------------------------------------------------ //


// ------------------------------------------------------------------------ //
// Behavioral Verilog Module for a MIPS-like ALU  //
// -- In Continuous and procedure assignments Verilog extends the smaller //
// operands by replicating their MSbit, if it is equal to x, z; otherwise //
// operends them with 0's. Arithmetic is interpreted as 2's C //
// -- regs are considered as unsigned bit-vectors but the all arithmetic  //
// is done in 2's complement. //
// // At ALU Instatiation time parameter n determines the ALU bit-size //
// ------------------------------------------------------------------------ //

// repetoire of operations for ALU, selected by ALU_ctr (change at will) //
`define ADD  4'b0111 // 2's compl add
`define ADDU 4'b0001 // unsigned add
`define SUB  4'b0010 // 2's compl subtract
`define SUBU 4'b0011 // unsigned subtract
`define AND  4'b0100 // bitwise OR
`define OR   4'b0101 // bitwise AND
`define XOR  4'b0110 // bitwise XOR
`define SLT  4'b1010 // set result=1 if less than 2's compl
`define SLTU 4'b1011 // set result=1 if less than unsigned
`define NOP  4'b0000 // do nothing
`define SLL  4'b1000  // shift left logical
`define SRL  4'b1001  // shift right logical
`define SRA  4'b1100  // shift right arithemetic
`define SZNE 4'b1101  // Set result to zero if operands are not equal
`define SZLT 4'b1110  // Set result to zero if ADin <= BDin
`define SZGT 4'b1111  // Set result to zero if ADin > BDin


module ALU_behav( ADin, BDin, ALU_ctr, Result, Overflow, Carry_in, Carry_out, Zero ); 

   parameter n = 32, Ctr_size = 4;

   integer i;

   input     Carry_in;
   input [Ctr_size-1:0] ALU_ctr;
   input [n-1:0] 	ADin, BDin;
   output [n-1:0] 	Result;
   reg [n-1:0] 		Result, tmp;
   output 		Carry_out, Overflow, Zero;
   reg 			Carry_out, Overflow, Zero;

   always @(ALU_ctr or ADin or BDin or Carry_in)
      begin
	 case(ALU_ctr)
	   `ADD:  begin
	      {Carry_out, Result} = ADin + BDin + Carry_in;
	      Overflow = ADin[n-1] & BDin[n-1] & ~Result[n-1]
			 | ~ADin[n-1] & ~BDin[n-1] & Result[n-1];
	   end
	   `ADDU:  {Overflow, Result} = ADin + BDin + Carry_in; 
	   `SUB:  begin
	      {Carry_out, Result} = ADin - BDin;
	      Overflow = ADin[n-1] & ~BDin[n-1] & Result[n-1]
			 | ~ADin[n-1] & BDin[n-1] & ~Result[n-1];
	   end
	   `SUBU: {Overflow, Result} = ADin - BDin;
	   `SLT:  begin
	      {Carry_out, tmp} = ADin - BDin;
	      Overflow = ADin[n-1] & ~BDin[n-1] & ~tmp[n-1] 
			 | ~ADin[n-1] & BDin[n-1] & tmp[n-1];
	      //$display("SLT:- [%d] tmp = %d [%b]; Cout=%b, Ovf=%b; A=%d, B=%d",
		//       $time, tmp, tmp, Carry_out, Overflow, ADin, BDin );
	      
	      Result = tmp[n-1] ^ Overflow;
	      //$display("SLT:+R=%d [%b]", Result, Result );

	   end
	   `SLTU: begin
	      {Carry_out, tmp} = ADin - BDin;
	      //$display("SLTU:- [%d] tmp = %d [%b]; Cout=%b, Ovf=%b; A=%d, B=%d",
		//       $time, tmp, tmp, Carry_out, Overflow, ADin, BDin );
	      Result = Carry_out;
	      //$display("SLTU:+R=%d [%b]", Result, Result );
	   end
	   `OR :  Result = ADin | BDin;
	   `AND:  Result = ADin & BDin;
	   `XOR: Result = ADin ^ BDin;
	   `SLL: begin
	      Result = ADin << BDin[10:6];
	   end
	   `SRL: begin
	      Result = ADin >> BDin[10:6];
	   end
	   `SRA: begin 
                Result = ADin;
                for (i = 0; i < BDin[10:6]; i = i + 1) begin
                  Result = Result >> 1;
                  Result[31] = ADin[31];
                 // $display("A=%h B=%h Result=%h",ADin,BDin[10:6],Result);
                end
	   end
           `SZNE:
             if (ADin != BDin) Result = 32'b0;
             else Result = 32'hFFFFFFFF;
           `SZGT:
             if (ADin > BDin) Result = 32'b0;
             else Result = 32'hFFFFFFFF;
           `SZLT:
             if (ADin <= BDin) Result = 32'b0;
             else Result = 32'hFFFFFFFF;
	   `NOP:  Result = ADin;
	 endcase
	 
	 Zero = ~| Result;  // Result = 32'b0
      end
endmodule

   

